package com.io.nio.mappedByteBuffer;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class Lock2 {

	static final int LENGTH = 0x7ffffff;
	static FileChannel fc;

	public static void main(String[] args) throws Exception {
		// 使用可随机访问文件创建可读写文件通道
		fc = new RandomAccessFile("lock.txt", "rw").getChannel();
		// 内存映射可读写文件，并映射至整个文件
		MappedByteBuffer out = fc
				.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
		for (int i = 0; i < LENGTH; i++) {
			out.put((byte) 'x');
		}
		// 锁定前1/2内容
		new LockAndModify(out, 0, 0 + LENGTH / 2);

		// 读
		new Read();

	}

	private static class Read extends Thread {

		FileLock flin = null; // 声明文件锁对象

		Read() throws Exception {
			int operateNum = 10; // 若文件锁一直别占用，设置最大读取次数为10次，超出次数表示文 //件不可读,
			for (int i = 0; i < operateNum; i++) {
				try {
					flin = fc.tryLock(); // 获取文件非阻塞锁，如果不能获取，继续等待0.5s.
					break;
				} catch (Exception e) {
					System.out.println("有其他线程正在操作该文件，当前线程休眠100毫秒");
					sleep(100);
					if ((i + 1) >= operateNum) {
						throw e; // 文件被强制占用 ，处于不可读的状态
					}
				}
			}
		}

	}

	private static class LockAndModify extends Thread {
		private ByteBuffer buff;
		private int start, end;

		LockAndModify(ByteBuffer mbb, int start, int end) {
			this.start = start;
			this.end = end;
			// 调整可最大读写位置
			mbb.limit(end);
			// 调整读写起始位置
			mbb.position(start);
			// 创建新的子缓冲区，但与原缓冲是共享同一片数据， 只是缓冲区位置、界限和标记值是相互独立的
			buff = mbb.slice();
			start();
		}

		public void run() {
			FileLock fl;
			try {
				// 锁住文件的某一部分,其中第三个参数表示是否是共享锁。
				// 获取独占锁，如果要锁定的部分被其他应用程序锁定，则会阻塞，至到获取锁为止
				fl = fc.lock(start, end, false);
				System.out.println("Locked:" + start + " to " + end);
				while (buff.position() < buff.limit() - 1) {
					buff.put((byte) (buff.get() + 1));
				}
				// JVM退出，或者channel关闭的时候会自动释放这些锁，但是你也可以用FileLock的release(
				// )方法，明确地释放锁，就像这里释放锁一样
				fl.release();
				System.out.println("released:" + start + " to " + end);
			} catch (Exception e) {
				System.out.println("占用");
				e.printStackTrace();
			}
		}
	}

}
